package drds.plus.parser.parser;

import drds.plus.parser.abstract_syntax_tree.expression.primary.misc.Identifier;
import drds.plus.parser.abstract_syntax_tree.expression.primary.misc.ParameterMarker;
import drds.plus.parser.abstract_syntax_tree.expression.primary.misc.Wildcard;
import drds.plus.parser.abstract_syntax_tree.statement.select.Limit;
import drds.plus.parser.lexer.Lexer;
import drds.plus.parser.lexer.Token;
import lombok.NonNull;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import static drds.plus.parser.lexer.Token.*;

public abstract class Parser {


    protected final Lexer lexer;
    protected final boolean cacheEvalRst;

    public Parser(Lexer lexer) {
        this(lexer, true);
    }

    public Parser(Lexer lexer, boolean cacheEvalRst) {
        this.lexer = lexer;
        this.cacheEvalRst = cacheEvalRst;
    }


    /**
     * <code>(id (',' id)*)?</code>
     *
     * @return never null or empty. {@link LinkedList} is possible
     */
    protected List<Identifier> buildIdentifierList() {
        return buildIdentifierList(identifier());
    }

    /**
     * @return type of {@link Wildcard} is possible. never null
     */
    public Identifier identifier() {
        //入口函数补齐token
        if (lexer.token() == null) {
            lexer.nextToken();
        }
        //
        Identifier identifier;
        lexer.token();
        match(IDENTIFIER);
        identifier = new Identifier(null, lexer.stringValue());
        identifier.setCacheEvalValue(cacheEvalRst);
        lexer.nextToken();
        if (lexer.token() == PUNC_DOT) {
            switch (lexer.nextToken()) {
                case IDENTIFIER:
                    identifier = new Identifier(identifier, lexer.stringValue());
                    identifier.setCacheEvalValue(cacheEvalRst);
                    lexer.nextToken();
                    break;
                case OP_ASTERISK:
                    Wildcard wildcard = new Wildcard(identifier);
                    wildcard.setCacheEvalValue(cacheEvalRst);
                    lexer.nextToken();
                    return wildcard;
                default:
                    throw error("expect id or * after '.'");
            }
        }
        return identifier;
    }

    protected List<Identifier> buildIdentifierList(Identifier identifier) {
        if (lexer.token() != Token.PUNC_COMMA) {//一般情况就是)
            List<Identifier> identifierList = new ArrayList<Identifier>(1);
            identifierList.add(identifier);
            return identifierList;
        }
        List<Identifier> identifierList = new LinkedList<Identifier>();
        identifierList.add(identifier);
        while (lexer.token() == Token.PUNC_COMMA) {
            lexer.nextToken();
            identifier = identifier();
            identifierList.add(identifier);
        }
        return identifierList;
    }

    protected ParameterMarker createParameterMarker(int index) {
        ParameterMarker parameterMarker = new ParameterMarker(index);
        parameterMarker.setCacheEvalValue(cacheEvalRst);
        return parameterMarker;
    }

    /**
     * nothing has been pre-consumed
     *
     * @return null if there is no order limit
     */
    protected Limit limit() {
        if (lexer.token() != KW_OFFSET) {
            return null;
        }
        lexer.nextToken();
        //
        Number num1;
        Number num2;
        int parameterIndex1;
        int parameterIndex2;
        switch (lexer.token()) {
            case LITERAL_NUM_INTEGER: {
                num1 = lexer.integerValue();
                lexer.nextToken();
                lexer.token();
                match(IDENTIFIER);
                if ("SIZE".equalsIgnoreCase(lexer.stringValue())) {
                    lexer.nextToken();
                    switch (lexer.token()) {
                        case LITERAL_NUM_INTEGER:
                            num2 = lexer.integerValue();
                            lexer.nextToken();
                            return new Limit(num1, num2);
                        case QUESTION_MARK:
                            parameterIndex2 = lexer.parameterIndex();
                            lexer.nextToken();
                            return new Limit(num1, createParameterMarker(parameterIndex2));
                        default:
                            throw error("expect digit or ? after , for limit");
                    }
                } else {
                    throw new IllegalArgumentException("只支持offset size语法");
                }

            }
            case QUESTION_MARK: {
                parameterIndex1 = lexer.parameterIndex();
                lexer.nextToken();
                lexer.token();
                match(IDENTIFIER);
                if ("SIZE".equalsIgnoreCase(lexer.stringValue())) {
                    lexer.nextToken();
                    switch (lexer.token()) {
                        case LITERAL_NUM_INTEGER:
                            num2 = lexer.integerValue();
                            lexer.nextToken();
                            return new Limit(createParameterMarker(parameterIndex1), num2);
                        case QUESTION_MARK:
                            parameterIndex2 = lexer.parameterIndex();
                            lexer.nextToken();
                            return new Limit(createParameterMarker(parameterIndex1), createParameterMarker(parameterIndex2));
                        default:
                            throw error("expect digit or ? after , for limit");
                    }
                } else {
                    throw new IllegalArgumentException("只支持offset size语法");
                }
            }
            default:
                throw error("expect digit or ? after limit");
        }
    }

    /**
     * @return index (start from 0) of expected text which is first matched. -1 if
     * none is matched.
     */
    protected int stringValueIndexOf(String... strings) {
        if (lexer.token() == IDENTIFIER) {
            String stringValue = lexer.stringValue();
            for (int i = 0; i < strings.length; ++i) {
                if (strings[i].equals(stringValue)) {
                    return i;
                }
            }
        }
        return -1;
    }

    protected int match(String... strings) {
        if (strings == null || strings.length <= 0) {
            throw new IllegalArgumentException("at least one expect token");
        }
        if (lexer.token() != IDENTIFIER) {
            throw error("期望是标识符");
        }
        String stringValue = lexer.stringValue();
        for (int i = 0; i < strings.length; ++i) {
            if (stringValue == null ? strings[i] == null : stringValue.equals(strings[i])) {
                return i;
            }
        }
        throw error("expect " + strings);
    }

    protected void match(@NonNull Token token) {
        if (lexer.token() == token) {
            return;
        } else {
            throw error("expect " + token + ",but " + lexer.token());
        }

    }

    protected RuntimeException error(String msg) {
        StringBuilder sb = new StringBuilder();
        sb.append(msg).append(".\n lexer: ").append(lexer);
        throw new RuntimeException(sb.toString());
    }

}
